package handler

import (
	"fmt"
	"lsgfish/server/internal/dto/request"
	"lsgfish/server/internal/service"
	"lsgfish/server/internal/tool"
	"strings"

	"os"
	"path/filepath"
	"time"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

// GcodeController 数据库代码生成控制器
type GcodeController struct {
	service service.GcodeService
}

// NewGcodeController 创建新的GcodeController实例
func NewGcodeController(db *gorm.DB) *GcodeController {
	return &GcodeController{
		service: service.NewGcodeService(db),
	}
}

// GetDatabaseStructure 获取数据库结构树
// @Summary 获取数据库结构树
// @Description 获取数据库和表的树状结构信息
//
// @Tags 数据库代码生成
//
// @Accept json
//
// @Produce json
//
// @Success 200 {object} tool.Response{data=[]response.DatabaseInfo} "获取成功"
//
// @Failure 500 {object} tool.Response "服务器错误"
//
// @Router /gcode/structure [get]
func (c *GcodeController) GetDatabaseStructure(ctx *gin.Context) {
	// 获取数据库结构
	result, err := c.service.GetDatabaseStructure()
	if err != nil {
		tool.Err(ctx, "获取数据库结构失败: "+err.Error())
		return
	}

	tool.Succ(ctx, "获取数据库结构成功", result)
}

// GetTableStructure 获取表结构
// @Summary 获取表结构
// @Description 获取指定表的详细结构信息
//
// @Tags 数据库代码生成
//
// @Accept json
//
// @Produce json
//
// @Param database query string true "数据库名称"
// @Param table query string true "表名称"
//
// @Success 200 {object} tool.Response{data=response.TableStructure} "获取成功"
//
// @Failure 400 {object} tool.Response "参数错误"
//
// @Failure 500 {object} tool.Response "服务器错误"
//
// @Router /gcode/table [get]
func (c *GcodeController) GetTableStructure(ctx *gin.Context) {
	// 获取参数
	database := ctx.Query("database")
	table := ctx.Query("table")

	if database == "" || table == "" {
		tool.Err(ctx, "数据库名和表名不能为空")
		return
	}

	// 获取表结构
	result, err := c.service.GetTableStructure(database, table)
	if err != nil {
		tool.Err(ctx, "获取表结构失败: "+err.Error())
		return
	}

	tool.Succ(ctx, "获取表结构成功", result)
}

// GenerateTableCode 生成表代码
// @Summary 生成表代码
// @Description 根据表结构生成CRUD代码并以ZIP流方式下载
//
// @Tags 数据库代码生成
//
// @Accept json
//
// @Produce application/zip
//
// @Param generateInfo body request.GenerateCodeRequest true "生成参数"
//
// @Success 200 {file} binary "生成的代码ZIP文件"
//
// @Failure 400 {object} tool.Response "参数错误"
//
// @Failure 500 {object} tool.Response "服务器错误"
//
// @Router /gcode/generate [post]
func (c *GcodeController) GenerateTableCode(ctx *gin.Context) {
	var req request.GenerateCodeRequest
	if err := ctx.ShouldBindJSON(&req); err != nil {
		tool.Err(ctx, "参数解析失败: "+err.Error())
		return
	}

	// 验证参数
	if len(req.Tables) == 0 {
		tool.Err(ctx, "表信息不能为空")
		return
	}

	// 检查是否为HTML表单提交
	isHtmlForm := false
	contentType := ctx.GetHeader("Content-Type")
	if contentType == "application/x-www-form-urlencoded" ||
		strings.HasPrefix(contentType, "multipart/form-data") {
		isHtmlForm = true
	}

	// 生成代码（不写入磁盘）
	generatedFiles, err := c.service.GenerateCodeInMemory(req)
	if err != nil {
		tool.Err(ctx, "生成代码失败: "+err.Error())
		return
	}

	// 设置响应头，使浏览器将响应识别为可下载的ZIP文件
	zipFileName := "generated_code.zip"

	// 检查前端请求头
	xRequestedWith := ctx.GetHeader("X-Requested-With")
	xDirectDownload := ctx.GetHeader("X-Direct-Download")

	// 如果指定了直接下载或不是AJAX请求，则直接返回数据
	if xDirectDownload == "true" || xRequestedWith == "" || isHtmlForm {
		// 普通请求或直接下载请求处理，直接提供文件下载
		ctx.Header("Content-Description", "File Transfer")
		ctx.Header("Content-Type", "application/octet-stream")
		ctx.Header("Content-Disposition", "attachment; filename="+zipFileName)
		ctx.Header("Content-Transfer-Encoding", "binary")
		ctx.Header("Expires", "0")
		ctx.Header("Cache-Control", "private, no-transform, no-store, must-revalidate")
		ctx.Header("Pragma", "no-cache")
		ctx.Header("Access-Control-Expose-Headers", "Content-Disposition, Content-Length, Content-Type")

		// 将生成的文件内容打包成ZIP并写入响应
		if err := tool.WriteZipToResponse(ctx.Writer, generatedFiles); err != nil {
			// 注意：此时响应头已经发送，无法再使用tool.Err方法
			fmt.Printf("生成ZIP文件失败: %v\n", err)
			return
		}
		return
	}

	// AJAX请求处理方式，通过提供下载URL来处理
	// 这里将生成的文件保存到临时目录，提供下载链接
	tempDir := "tmp/downloads"
	if err := os.MkdirAll(tempDir, 0755); err != nil {
		tool.Err(ctx, "创建临时目录失败: "+err.Error())
		return
	}

	// 生成唯一的文件ID
	fileID := fmt.Sprintf("%d", time.Now().UnixNano())
	zipFilePath := filepath.Join(tempDir, fileID+".zip")

	// 创建临时ZIP文件
	zipFile, err := os.Create(zipFilePath)
	if err != nil {
		tool.Err(ctx, "创建临时文件失败: "+err.Error())
		return
	}
	defer zipFile.Close()

	// 将内容写入临时ZIP文件
	if err := tool.WriteZipToResponse(zipFile, generatedFiles); err != nil {
		zipFile.Close()
		os.Remove(zipFilePath)
		tool.Err(ctx, "生成ZIP文件失败: "+err.Error())
		return
	}

	// 关闭文件
	zipFile.Close()

	// 返回下载URL
	downloadURL := fmt.Sprintf("/download/%s/%s", fileID, zipFileName)
	tool.Succ(ctx, "代码生成成功，请下载", gin.H{
		"downloadUrl": downloadURL,
		"fileId":      fileID,
		"fileName":    zipFileName,
	})
}

// DownloadGeneratedCode 下载生成的代码文件
// @Summary 下载生成的代码文件
// @Description 根据文件ID下载之前生成的代码文件
//
// @Tags 数据库代码生成
//
// @Accept json
//
// @Produce application/octet-stream
//
// @Param fileId path string true "文件ID"
// @Param fileName path string true "文件名"
//
// @Success 200 {file} binary "生成的代码ZIP文件"
//
// @Failure 400 {object} tool.Response "参数错误"
//
// @Failure 404 {object} tool.Response "文件不存在"
//
// @Failure 500 {object} tool.Response "服务器错误"
//
// @Router /download/{fileId}/{fileName} [get]
func (c *GcodeController) DownloadGeneratedCode(ctx *gin.Context) {
	// 获取文件ID和文件名
	fileID := ctx.Param("fileId")
	fileName := ctx.Param("fileName")

	if fileID == "" || fileName == "" {
		tool.Err(ctx, "文件ID或文件名不能为空")
		return
	}

	// 构建文件路径
	tempDir := "tmp/downloads"
	zipFilePath := filepath.Join(tempDir, fileID+".zip")

	// 检查文件是否存在
	if _, err := os.Stat(zipFilePath); os.IsNotExist(err) {
		tool.Err(ctx, "文件不存在或已过期，请重新生成")
		return
	}

	// 设置响应头
	ctx.Header("Content-Description", "File Transfer")
	ctx.Header("Content-Type", "application/octet-stream")
	ctx.Header("Content-Disposition", "attachment; filename="+fileName)
	ctx.Header("Content-Transfer-Encoding", "binary")
	ctx.Header("Expires", "0")
	ctx.Header("Cache-Control", "private, no-transform, no-store, must-revalidate")
	ctx.Header("Pragma", "no-cache")

	// 使用Gin的文件系统提供文件下载
	ctx.File(zipFilePath)

	// 下载完成后删除临时文件(可选)
	// 如果想保留文件一段时间，可以注释掉这行，并实现一个定时任务清理过期文件
	// go func() {
	// 	time.Sleep(time.Minute * 5) // 等待5分钟后删除
	// 	os.Remove(zipFilePath)
	// }()
}
